package com.gking.centerSystem.utils;


import com.gking.centerSystem.utils.commonUtil.CustomEncryptorUtil;
import com.gking.centerSystem.utils.commonUtil.JwtUtil;
import com.gking.centerSystem.utils.commonUtil.RandomGenerator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.gking.centerSystem.common.RedisConstants.*;

/**
 * 通过 HttpServletRequest 获取登录用户相关信息，以及控制用户的登录信息
 */
@Slf4j
public class GetAuthInfoUtil {

    private final StringRedisTemplate stringRedisTemplate;

    private final HttpServletRequest request;

    // 登录用户服务端缓存过期时间
    private static final long Login_User_Cache_Server_TTL = 20L;

    public GetAuthInfoUtil(StringRedisTemplate stringRedisTemplate, HttpServletRequest request) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.request = request;
    }


    /**
     * 通过用户的登录token获取用户token中的明文信息
     *
     * @return 登录用户的 userKey
     */
    public String getTokenUserKey() {
        String token = request.getHeader("Authorization");
        return CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
    }


    /**
     * 验证登录用户
     *
     * @return true: 验证通过 false: 验证失败
     */
    public boolean verifyLoginUser() {
        String userServerInfo = getUserServerInfo();
        String userDeviceInfo = getUserDeviceInfo();
        String userLoginIp = getUserLoginIp();

        String clientIp = getClientIp(request);
        String clientDeviceInfo = getClientDeviceInfo(request);

        // 存储在服务端的用户信息是否存在
        if (userServerInfo == null || userLoginIp == null || userDeviceInfo == null) return false;
        // 当前登录用户设备id和登录时记录的设备id是否一致，防止token盗取（访问设备一致性）
        if (!userDeviceInfo.equals(clientDeviceInfo)) return false;
        // 当前登录用户访问IP和登录时记录的访问IP是否一致，防止token盗取（访问IP一致性）
        if (!userLoginIp.equals(clientIp)) return false;

        return true;
    }

    /**
     * 设置服务端登录用户缓存信息
     *
     * @param userKey  登录用户唯一凭证
     * @param userId   用户id
     * @param deviceId 设备id
     * @param loginIp  登录ip
     */
    public void setUserCacheInfo(String userKey, String userId, String deviceId, String loginIp) {
        setUserServerInfo(userKey, userId);
        setUserDeviceInfo(userKey, deviceId);
        setUserLoginIp(userKey, loginIp);
    }

    /**
     * 清空服务端登录用户缓存信息
     */
    public void clearUserCacheInfo() {
        String userKey = getTokenUserKey();

        stringRedisTemplate.delete(Login_User_Server_Key + userKey);
        stringRedisTemplate.delete(Login_User_Equipment + userKey);
        stringRedisTemplate.delete(Login_User_Ip + userKey);

        // 删除用户系统路由缓存
        Set<String> keys = stringRedisTemplate.keys(Login_User_Routes + userKey + "*");
        stringRedisTemplate.delete(keys);
        // 删除角色、权限缓存
        stringRedisTemplate.delete(Login_User_Roles + userKey);
        stringRedisTemplate.delete(Login_User_Permissions + userKey);
    }

    /**
     * 重置用户服务端登录信息（服务端用户信息保活 keep-alive）
     */
    public void keepAliveUserServerInfo() {
        String userKey = getTokenUserKey();

        String userServerInfo = getUserServerInfo();
        String userDeviceInfo = getUserDeviceInfo();
        String userLoginIp = getUserLoginIp();

        setUserServerInfo(userKey, userServerInfo);
        setUserDeviceInfo(userKey, userDeviceInfo);
        setUserLoginIp(userKey, userLoginIp);
    }

    /**
     * 获取客户端token中携带的验证吗
     *
     * @return token携带的验证码
     */
    public String getUserVerifyCode() {
        try {
            String userKey = getTokenUserKey();
            // userKey 前4位为当前用户登录时的验证码
            assert userKey != null;
            return userKey.substring(0, 4);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 通过用户的登录token获取用户服务端登录用户信息
     *
     * @param token token
     * @return 用户id
     */
    public String getUserServerInfoByToken(String token) {
        String userKey = CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
        return stringRedisTemplate.opsForValue().get(Login_User_Server_Key + userKey);
    }


    /**
     * 用户登录时设置服务端登录用户信息
     *
     * @param userKey  用户索引键
     * @param userInfo 用户信息 (用户id)
     */
    public void setUserServerInfo(String userKey, String userInfo) {
// TODO 添加try catch
        stringRedisTemplate.opsForValue().set(
                Login_User_Server_Key + userKey,
                userInfo,
                Login_User_Cache_Server_TTL,
                TimeUnit.MINUTES);
    }

    /**
     * 通过用户的登录token获取用户服务端登录用户信息
     *
     * @return 用户id
     */
    public String getUserServerInfo() {
        String userKey = getTokenUserKey();
        return stringRedisTemplate.opsForValue().get(Login_User_Server_Key + userKey);
    }


    /**
     * 用户登录时设置服务端登录用户设备信息
     *
     * @param userKey    用户索引键
     * @param deviceInfo 设备信息
     */
    public void setUserDeviceInfo(String userKey, String deviceInfo) {
        try {
            stringRedisTemplate.opsForValue().set(
                    Login_User_Equipment + userKey,
                    deviceInfo,
                    Login_User_Cache_Server_TTL,
                    TimeUnit.MINUTES);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过用户的登录token获取服务端用户设备id
     *
     * @return 用户登录设备id
     */
    public String getUserDeviceInfo() {
        String userKey = getTokenUserKey();
        return stringRedisTemplate.opsForValue().get(Login_User_Equipment + userKey);
    }


    /**
     * 用户登录时设置服务端登录用户ip
     *
     * @param userKey 用户索引键
     */
    public void setUserLoginIp(String userKey, String ip) {
        stringRedisTemplate.opsForValue().set(
                Login_User_Ip + userKey,
                ip,
                Login_User_Cache_Server_TTL,
                TimeUnit.MINUTES);
    }

    /**
     * 通过用户的登录token获取用户服务端登录用户ip
     *
     * @return 用户登录存储的IP
     */
    public String getUserLoginIp() {
        String userKey = getTokenUserKey();
        return stringRedisTemplate.opsForValue().get(Login_User_Ip + userKey);
    }


    /**
     * 生成用户登录时的凭证，用来作为 JWT 的明文信息
     *
     * @param verificationCode 验证码
     * @return userKey 用户登录时生成的凭证
     */
    public static String setUserKey(String verificationCode) {
        return verificationCode + RandomGenerator.generateRandomString(12);
    }

    /**
     * 通过用户的登录token获取用户token中的明文信息
     *
     * @return 登录用户的 userKey
     */
    public static String getClientTokenUserKey(String token) {
        try {
            return CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取客户端token中携带的验证吗
     *
     * @param token token
     * @return token携带的验证码
     */
    public static String getClientVerifyCode(String token) {
        try {
            String userKey = CustomEncryptorUtil.decrypt(JwtUtil.verifyToken(token));
            // userKey 前4位为当前用户登录时的验证码
            assert userKey != null;
            return userKey.substring(0, 4);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取客户端ip地址，支持获取通过nginx代理服务器设置的真实IP
     *
     * @param request 请求
     * @return 客户端ip地址
     */
    public static String getClientIp(HttpServletRequest request) {

//        Enumeration<String> headerNames = request.getHeaderNames();
//        while (headerNames.hasMoreElements()) {
//            String headerName = headerNames.nextElement();
//            log.info("Header: {} = {}", headerName, request.getHeader(headerName));
//        }

        String remoteAddr = "";

        if (request != null) {
            remoteAddr = request.getHeader("X-Forwarded-For");
            if (remoteAddr == null || remoteAddr.isEmpty() || "unknown".equalsIgnoreCase(remoteAddr)) {
                remoteAddr = request.getHeader("X-Real-IP");
            }
            if (remoteAddr == null || remoteAddr.isEmpty() || "unknown".equalsIgnoreCase(remoteAddr)) {
                remoteAddr = request.getRemoteAddr();
            }
        }

        return remoteAddr;
    }

    /**
     * 获取客户端设备信息
     *
     * @param request 请求
     * @return 客户端设备信息
     */
    public static String getClientDeviceInfo(HttpServletRequest request) {
        try {
            return request.getHeader("c-device-id");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}
